package com.youxin.cpcenter.common.log;


import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.youxin.busfoundation.utils.mybatis.BasePage;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.BeanUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.concurrent.ListenableFutureCallback;

import com.youxin.busfoundation.bean.BaseLogBean;
import com.youxin.busfoundation.bean.BaseRequestBean;
import com.youxin.busfoundation.bean.ResponseObject;
import com.youxin.busfoundation.bean.log.RequestLogBean;
import com.youxin.busfoundation.bean.login.UserInfo;
import com.youxin.busfoundation.common.BuildConfig;
import com.youxin.busfoundation.common.GlobalConfig;
import com.youxin.busfoundation.common.LoginAuth;
import com.youxin.busfoundation.enums.SystemType;
import com.youxin.busfoundation.net.WebVisitor;
import com.youxin.foundation.db.utils.KeyGenerationUtils;
import com.youxin.foundation.utils.ClassUtils;
import com.youxin.foundation.utils.ExceptionUtils;
import com.youxin.foundation.utils.JsonUtils;

/**
 * 日志切
 * 
 * @author yingde.cao
 *
 */
@Component
@Aspect
public class LogAspect {
	private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(LogAspect.class);

	@Pointcut("@annotation(com.youxin.cpcenter.common.log.LogMethod)")
	public void annotationPointCut() {
	}

	// @Before("annotationPointCut()")
	// public void before(JoinPoint joinPoint) {
	//
	// }
	/**
	 * 保存日志
	 * 
	 * @description
	 * @param joinPoint
	 */
	private void saveLog(JoinPoint joinPoint, Object retValue, Throwable throwable) {
		try {
			MethodSignature sign = (MethodSignature) joinPoint.getSignature();
			Method method = sign.getMethod();
			Class<?>[] parameterTypes = method.getParameterTypes();
			RequestLogBean logBean = new RequestLogBean();

			logBean.setFlag(BuildConfig.getFlagType().getValue());
			LogMethod logMethod = method.getAnnotation(LogMethod.class);
			String identifier = logMethod.id();
			Object[] signatureArgs = joinPoint.getArgs();
			/**
			 * 判断参数是否为空
			 */
			BaseRequestBean baseRequestBean = null;
			if (parameterTypes.length > 0) {
				Class<?> lastCls = parameterTypes[parameterTypes.length - 1];
				Object lastParameter = signatureArgs[parameterTypes.length - 1];
				baseRequestBean = (lastParameter instanceof BaseRequestBean) ? (BaseRequestBean) lastParameter : null;
				/**
				 * 是否包含日志参数,如果包含拷贝过来
				 */
				if (baseRequestBean != null) {
					Method logBeanMethod = findLogBeanMethod(lastCls);
					if (logBeanMethod != null) {
						Object returnValue = logBeanMethod.invoke(lastParameter);
						if (returnValue != null) {
							BeanUtils.copyProperties(returnValue, logBean);
						}
					}
					logBean.setTraceId(baseRequestBean.getTrackingId());

				}

				Object lastObject = signatureArgs[signatureArgs.length - 1];

				String[] eventContents = logMethod.eventContent();

				if (eventContents != null && eventContents.length > 0 && !lastCls.equals(HttpServletResponse.class)) {
					Map<String, String> propertyMap = new HashMap<>();
					for (String propertyName : eventContents) {
						try {
							String[] properties = StringUtils.split(propertyName, '.');
							if (properties.length > 0) {
								Object result = ClassUtils.findPropertyMethod(lastObject, lastCls, properties, 0);
								if (result != null) {
									propertyMap.put(properties[properties.length - 1], result.toString());
								}
							}
						} catch (Exception e) {
						}
					}
					if (propertyMap.size() > 0) {
						logBean.setEventContent(JsonUtils.mapToJsonString(propertyMap));
					}
				}
			}
			if (baseRequestBean != null) {
				String jsonValue=null;
				if (baseRequestBean instanceof BasePage<?>) {
					try {
						BasePage<?> basePage =(BasePage<?>)((BasePage<?>) baseRequestBean).clone();
						basePage.setResults(null);
						jsonValue=JsonUtils.objectToFasterxmlJsonString(basePage);
					} catch (Exception e) {
						// TODO: handle exception
					}					
				}else{
					jsonValue=JsonUtils.objectToFasterxmlJsonString(baseRequestBean);
				}
				if(jsonValue!=null&&jsonValue.length()<=1000){
					logBean.setParams(jsonValue);
				}
			}

			if (retValue != null) {
				if (retValue instanceof ResponseObject) {
					ResponseObject responseObject = (ResponseObject) retValue;
					String jsonValue = JsonUtils.objectToFasterxmlJsonString(responseObject);
					if(jsonValue!=null&&jsonValue.length()<=1000){
						logBean.setMsg(jsonValue);
					}
					logBean.setCode(responseObject.getCode());
				}
			} else if (throwable != null) {
				String exceptString = ExceptionUtils.getStackTrace(throwable);
				logBean.setDescription(exceptString);
			}
			logBean.setFlag(BuildConfig.getFlagType().getValue());
			logBean.setSystemType(SystemType.CP.getValue());
			logBean.setIdentifier(identifier);
			UserInfo userInfo=LoginAuth.getUserInfo();
			if(userInfo!=null)
			{
				logBean.setUserId(String.valueOf(userInfo.getEntMemberId()));
				logBean.setUserName(userInfo.getUsername());
			}
			saveLog(logBean);
		} catch (Exception e) {
			logger.error("日志保存异常before:", e);
		}
	}

	@SuppressWarnings("resource")
	static String extractPostRequestBody(HttpServletRequest request) {
		if ("POST".equalsIgnoreCase(request.getMethod())) {
			ServletInputStream stream = null;
			Scanner s = null;
			try {
				stream = request.getInputStream();
				s = new Scanner(stream, "UTF-8").useDelimiter("\\A");
				return s.hasNext() ? s.next() : "";
			} catch (Exception e) {
				// TODO: handle exception
			} finally {
				try {
					s.close();
				} catch (Exception e2) {
					// TODO: handle exception
				}
				try {
					stream.close();
				} catch (Exception e2) {
					// TODO: handle exception
				}
			}

		}
		return "";
	}

	private Object getTrackingIdObject(Object[] signatureArgs, Class<?>[] parameterTypes) {
		if (signatureArgs.length > 0) {
			Object lastObject = signatureArgs[signatureArgs.length - 1];
			if (lastObject instanceof BaseRequestBean) {
				Object trackingIdObj = ClassUtils.findPropertyMethod(lastObject,
						parameterTypes[parameterTypes.length - 1], "trackingId");
				return trackingIdObj;
			}
		}
		return null;
	}

	/**
	 * 获取HttpServletResponse参数
	 * 
	 * @param signatureArgs
	 *            参数列表
	 * @return HttpServletResponse
	 */
	private HttpServletResponse findHttpResponse(Object[] signatureArgs) {
		for (Object obj : signatureArgs) {
			if (obj instanceof HttpServletResponse) {
				return (HttpServletResponse) obj;
			}
		}
		return null;
	}

	private HttpServletRequest findHttpRequest(Object[] signatureArgs) {
		for (Object obj : signatureArgs) {
			if (obj instanceof HttpServletRequest) {
				return (HttpServletRequest) obj;
			}
		}
		return null;
	}

	@After("annotationPointCut()")
	public void after(JoinPoint joinPoint) {
		// MethodSignature sign = (MethodSignature) joinPoint.getSignature();
		// Method method = sign.getMethod();
	}

	@AfterReturning(pointcut = "annotationPointCut()", returning = "retVal")
	public void afterReturning(JoinPoint joinPoint, Object retVal) {
		long traceId = 0;
		try {
			MethodSignature sign = (MethodSignature) joinPoint.getSignature();
			Method method = sign.getMethod();
			Class<?>[] parameterTypes = method.getParameterTypes();
			Object[] signatureArgs = joinPoint.getArgs();
			Object trackingIdObj = getTrackingIdObject(signatureArgs, parameterTypes);
			if (retVal instanceof ResponseObject) {
				ResponseObject result = (ResponseObject) retVal;
				if (trackingIdObj != null) {
					traceId = Long.valueOf(trackingIdObj.toString());
					if (traceId == 0) {
						traceId = KeyGenerationUtils.generateKey();
						result.setTrackingId(traceId);
					} else {
						result.setTrackingId(traceId);
					}
				} else {
					result.setTrackingId(KeyGenerationUtils.generateKey());
				}
			}
		} catch (Exception e) {
		}

		saveLog(joinPoint, retVal, null);
	}

	// @Around("within(com.youxin.cpcenter.controller.*)")
	// public Object processTx(ProceedingJoinPoint jp)
	// throws java.lang.Throwable
	// {
	// System.out.println("执行目标方法之前，模拟开始事务...");
	// // 获取目标方法原始的调用参数
	// Object[] args = jp.getArgs();
	//// if(args != null && args.length > 1)
	//// {
	//// // 修改目标方法的第一个参数
	//// args[0] = "【增加的前缀】" + args[0];
	//// }
	// // 以改变后的参数去执行目标方法，并保存目标方法执行后的返回值
	// Object rvt = jp.proceed(args);
	// System.out.println("执行目标方法之后，模拟结束事务...");
	// // 如果rvt的类型是Integer，将rvt改为它的平方
	//// if(rvt != null && rvt instanceof Integer)
	//// rvt = (Integer)rvt * (Integer)rvt;
	// return rvt;
	// }

	@AfterThrowing(pointcut = "annotationPointCut()", throwing = "e")
	public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
		// MethodSignature sign = (MethodSignature) joinPoint.getSignature();
		// Method method = sign.getMethod();

		saveLog(joinPoint, null, e);
	}

	private Method findLogBeanMethod(Class<?> cls) {
		Method[] methods = cls.getMethods();
		for (Method method : methods) {
			if (BaseLogBean.class.equals(method.getReturnType())) {
				return method;
			}
		}
		return null;
	}

	/**
	 * 保存日志
	 * 
	 * @param identifier
	 *            标识
	 */
	private void saveLog(RequestLogBean logBean) {
		try {
			WebVisitor.instance().httpPostEntityAsyncWithObject(GlobalConfig.LOG_URL, RequestLogBean.class, logBean,
					new ListenableFutureCallback<ResponseEntity<String>>() {
						@Override
						public void onSuccess(ResponseEntity<String> result) {
							logger.debug(result);
						}

						@Override
						public void onFailure(Throwable t) {
							logger.error("日志保存异常Web:", t);
						}
					});
		} catch (Exception e) {
			logger.error("日志保存异常Web:", e);
		}

	}

}
